package org.utils.plugins;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Hashtable;

import org.apache.log4j.Logger;
import org.utils.common.InitException;
import org.utils.io.GenericFileFilter;
import org.utils.xml.XMLConfigurable;
import org.utils.xml.XMLDataNode;
import org.utils.xml.XMLDataNodeParser;

/**
 * Created on 30 mai 2003
 * @author Manuel EVENO
 */
public class PluginManager implements XMLConfigurable
{
	public final String DEFAULT_PLUGIN_CONFIG_FILE = "plugin.xml";

	private PluginContext pluginContext = null;
	private Hashtable plugins = null;
	private MutableURLClassLoader classLoader = null;
	private Logger logger = Logger.getLogger(getClass());
	/**
	 * Default Constructor for PluginManager
	 */
	public PluginManager()
	{
		super();
		plugins = new Hashtable();
		classLoader = MutableURLClassLoader.newMutableInstance();
	}
	/**
	 * @see org.utils.xml.XMLConfigurable#configure(org.utils.xml.XMLDataNode)
	 */
	public void configure(XMLDataNode config) throws InitException
	{
		pluginContext = new PluginContext();
		pluginContext.setPluginPath(config.getAttribute("pluginPath"));
	}
	/**
	 *
	 */
	public PluginContext getPluginContext()
	{
		return pluginContext;
	}
	/**
	 * @param context
	 */
	public void setPluginContext(PluginContext context)
	{
		pluginContext = context;
	}
	/**
	 *
	 */
	public void loadPlugins() throws InitException
	{
		loadPlugins(pluginContext);
	}
	/**
	 * Loads the plugins with the specified context
	 * @param pContext
	 * @throws InitException
	 */
	public void loadPlugins(PluginContext pContext) throws InitException
	{
		File path = new File(pContext.getPluginPath());
		if (!path.exists() || !path.isDirectory())
			throw new InitException("Invalid Plugin Path: "+pContext.getPluginPath());
		GenericFileFilter filter = new GenericFileFilter();
		filter.setAcceptDirectories(true);
		filter.setAcceptByDefault(false);
		File[] dirs = path.listFiles((FileFilter)filter);
		for (int i = 0; i < dirs.length; i++)
		{
			File pluginDir = dirs[i];
			logger.debug("Looking for plugin in "+pluginDir);
			File pluginConfigFile = new File(pluginDir, DEFAULT_PLUGIN_CONFIG_FILE);
			logger.debug("Looking for file "+pluginConfigFile);
			if (!pluginConfigFile.exists() || pluginConfigFile.isDirectory())
				continue ;
			logger.debug("Found plugin.xml");
			loadPlugin(pContext, pluginConfigFile);
		}
	}
	/**
	 * Loads a plugin with the specified configuration file and context
	 * @param pContext
	 * @param pluginConfigFile
	 */
	public void loadPlugin(PluginContext pContext, File pluginConfigFile)
	{
		try
		{
			XMLDataNodeParser parser = new XMLDataNodeParser();
			XMLDataNode pluginConfig = null;
			pluginConfig = parser.parse(new FileInputStream(pluginConfigFile));

			// Load Plugin Jar containing plugin classes
			String jarFilename = pluginConfig.getChild("classpath").getAttribute("value");
			URL jarFile = new File(pluginConfigFile.getParentFile(), jarFilename).toURL();
			classLoader.addURL(jarFile);

			// Load Plugin Main Class
			Class c = classLoader.loadClass(pluginConfig.getAttribute("classname"));
			//Class c = Class.forName(pluginConfig.getAttribute("classname"));
			Plugin plugin = (Plugin) c.newInstance();
			plugin.configure(pluginConfig);
			plugin.setPluginContext(pContext);
			registerPlugin(plugin);
		}
		catch (Exception e)
		{
			logger.warn("Can't load plugin", e);
			//throw new InitException("Can't load plugin", e);
		}
	}
	/**
	 * Registers a plugin
	 * @param plugin
	 */
	public void registerPlugin(Plugin plugin)
	{
		plugins.put(plugin.getName(), plugin);
	}
	/**
	 * Gets a plugin by name
	 * @param name
	 * @return Plugin
	 */
	public Plugin getPlugin(String name)
	{
		return (Plugin) plugins.get(name);
	}
	/**
	 * Returns the collection of all registered plugins.
	 * @return The collection of all registered plugins.
	 */
	public Collection getPlugins()
	{
		return plugins.values();
	}
}
